/*
 * Copyright 2006-2008 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.martinimix.context.support;

import java.util.Locale;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.context.NoSuchMessageException;

import com.bluemartini.dna.BMMessages;
import com.bluemartini.dna.DNAList;

/**
 * Provides a Blue Martini DNA based Spring message source.
 *   
 * @author Scott Rossillo
 *
 */
public class BlueMartiniMessageSource implements MessageSource {
	
	private final static Log log = LogFactory.getLog(BlueMartiniMessageSource.class);
	
	/**
	 * Converts the given replacement arguments into a DNA list.  While Blue Martini
	 * supports named parameters, Spring message support relies on an argument list
	 * based replacement strategy.  This class still relies on Blue Martini to
	 * perform the actual replacement, however, named arguments cannot be supported.
	 * 
	 * <p>
	 * Therefore, given the array <code>{"foo", "bar"}</code>, a <code>DNAList</code>
	 * will be returned such that:
	 * </p>
	 * 
	 * <pre>
	 *   DNA {
	 *   	"1" String "foo",
	 *   	"2" String "bar"
	 *   }
	 * </pre>
	 *  
	 * @param args the array of <code>Object</code>s to be converted to DNA
	 * replacements
	 * 
	 * @return a <code>DNAList</code> containing the given <code>args</code>
	 */
	protected final DNAList convertReplacements(Object[] args) {
		
		final DNAList replacements = new DNAList();
		
		if(args != null) {
			for(int i = 0; i < args.length; i++) {
				replacements.setString(String.valueOf(i), args[i].toString());
			}
		}
		
		return replacements;
	}

	/* (non-Javadoc)
	 * @see org.springframework.context.MessageSource#getMessage(org.springframework.context.MessageSourceResolvable, java.util.Locale)
	 */
	public String getMessage(MessageSourceResolvable resolvable, Locale locale)
			throws NoSuchMessageException {
		
		String codes[] = resolvable.getCodes();
		
		for(int i = 0; i< codes.length; i++) {
			try { 
				return this.getMessage(codes[i], resolvable.getArguments(), locale);
			} catch(NoSuchMessageException e) {
				// ignore
			}
		}
		
		return resolvable.getDefaultMessage();
	}

	/* (non-Javadoc)
	 * @see org.springframework.context.MessageSource#getMessage(java.lang.String, java.lang.Object[], java.util.Locale)
	 */
	public String getMessage(String code, Object[] args, Locale locale)
			throws NoSuchMessageException {
		
		final DNAList replacements = convertReplacements(args);
		String msg;
		
		// BM will throw an NPE if code is null, so fail gracefully here instead
		if(code == null) {
			throw new IllegalArgumentException("Message code cannot be null!");
		}
		
		if(log.isDebugEnabled()) {
			log.debug("Message request for [" + code + "], locale [" + locale.getLanguage() + "]");
		}
		
		msg = this.getError(code, replacements, locale);
		
		if(msg == null) {
			msg = this.getLabel(code, replacements, locale);
		}
		
		if(msg == null) {
			msg = this.getUserMessage(code, replacements, locale);
		}
		
		// if no message is found, throw an exception
		if(msg == null) {
			throw new NoSuchMessageException("Cannot find a message for " + code);
		}
		
		return msg;
	}

	/* (non-Javadoc)
	 * @see org.springframework.context.MessageSource#getMessage(java.lang.String, java.lang.Object[], java.lang.String, java.util.Locale)
	 */
	public String getMessage(String code, Object[] replacements, String defaultMessage, Locale locale) {
		
		String msg = defaultMessage;
		
		try {
			msg = this.getMessage(code, replacements, locale);
		} catch(NoSuchMessageException e) { 
			// use default
		}
		
		return msg;
	}
	
	/**
	 * Returns the error identified by the given code.
	 * 
	 * @param code the message code to look up
	 * @param replacements a <code>DNAList</code> of text replacements to be filled in for
	 * parameters within the message
	 * 
	 * @param locale the <code>Locale</code> in which to do the lookup
	 * 
	 * @return the resolved message if found; <code>null</code> otherwise
	 * 
	 * @see BMMessages#getError(String, DNAList, String)
	 */
	protected final String getError(String code, DNAList replacements, Locale locale) {
		
		String message = BMMessages.getError(code, replacements, locale.getLanguage());
		
		return !code.equals(message) ? message : null; 
	}
	
	/**
	 * Returns the label identified by the given code.
	 * 
	 * @param code the message code to look up
	 * @param replacements a <code>DNAList</code> of text replacements to be filled in for
	 * parameters within the message
	 * 
	 * @param locale the <code>Locale</code> in which to do the lookup
	 * 
	 * @return the resolved message if found; <code>null</code> otherwise
	 * 
	 * @see BMMessages#getLabel(String, DNAList, String)
	 */
	protected final String getLabel(String code, DNAList replacements, Locale locale) {
		
		String message = BMMessages.getLabel(code, replacements, locale.getLanguage());
		
		return !code.equals(message) ? message : null; 
	}
	
	/**
	 * Returns the user message identified by the given code.
	 * 
	 * @param code the message code to look up
	 * @param replacements a <code>DNAList</code> of text replacements to be filled in for
	 * parameters within the message
	 * 
	 * @param locale the <code>Locale</code> in which to do the lookup
	 * 
	 * @return the resolved message if found; <code>null</code> otherwise
	 */
	protected final String getUserMessage(String code, DNAList replacements, Locale locale) {
		
		String message = BMMessages.getUserMessage(code, replacements, locale.getLanguage());
		
		return !code.equals(message) ? message : null; 
	}

}
